const fp = require('lodash/fp');
const { Maybe, Container } = require('./support')

/********模块一:函数式编程与JS异步编程，手写Promise**********/

/*
简答题
1.谈谈你是如何理解 JS 异步编程的  Eventloop、消息队列都是做什么的，什么是宏任务，什么是微任务？
答：
  a.js一开始设计的时候就是单线程的，而浏览器是多线程的，js中的任务分为同步任务和异步任务，
  当js在执行的时候，遇到异步任务会放到浏览器的不同线程里面等待执行，异步任务结束放到任务队列，等待执行。
  常见的异步编程方式有回调函数，settimeout,promise，事件监听，发布订阅。

  b.js在执行的过程中，遇到同步任务会将其放到调用栈执行，遇到异步任务会将其挂起到浏览器的线程就行等待，
  等待异步任务执行完毕或者倒计时结束，会将其异步任务的回调函数放到一个队列里面等待，
  这个队列就是消息队列。如果这个时候执行栈为空，就会去任务队列取一个任务放到执行栈执行。这样周而复始的过程，就是eventloop

  c.js里面的任务又分为宏任务和微任务之分，当eventloop调度的时候，会先去看任务队列有没有微任务，如果有的话，就会先执行微任务，直到其执行完毕，再执行宏任务。
    但是宏任务/微任务的执行过程又会产生新的宏任务/微任务，如果产生宏任务，就将其加入到宏任务队列等待执行，如果是微任务，就将其插入到当前的微任务队列，等待执行。
    一般会产生宏任务的方式有settimeout,setinterval
    一般会产生微任务的方式有promise,mutaionobserver,node.js里面的process.nexttick
*/

/*
2.将下面异步代码使用 Promise 的方式改进
*/

/*
new Promise((resolve, reject) => {
  setTimeout(() => {
    var a = "hello ";
    resolve(a);
  }, 1000)
}).then((value) => {
  return new Promise((r, j) => {
    setTimeout(() => {
      var b = "lagou ";
      r(value + b);
    }, 1000)
  })
}).then(value => {
  setTimeout(() => {
    var c = "I ❤️ U";
    console.log(value + c);
  }, 1000)
})
*/

/*
3.根据数据练习以下四道题
*/

const cars = [
  { name: 'Ferrari FF', horsepower: '660r', dollar_value: 700000, in_stock: true },
  { name: 'Spyker C12 zagato', horsepower: 650, dollar_value: 648000, in_stock: false },
  { name: 'Jaguar XKR-S', horsepower: 550, dollar_value: 132000, in_stock: false },
  { name: 'Audi R8', horsepower: 525, dollar_value: 114200, in_stock: false },
  { name: 'Aston_Martin One-77', horsepower: 750, dollar_value: 1850000, in_stock: true },
  { name: 'Pagani Huayra', horsepower: 700, dollar_value: 1300000, in_stock: false }
]

// 练习 1: 使用函数组合 fp.Flowright 重新实现函数
const isLastInStock = fp.flowRight(fp.prop('in_stock'), fp.last);
console.log(isLastInStock(cars));

// 练习 2: 使用 fp.flowRight, fp.prop 和 fp.first获取第一个Car 的 name
const getFirstCarName = fp.flowRight(fp.prop('name'), fp.first);
console.log(getFirstCarName(cars));

// 练习 3: 使用帮助函数_average 重构 averageDollarValue，使用函数组合的方式实现
let _average = function (xs) {
  return fp.reduce(fp.add, 0, xs) / xs.length;
}

let _dollarValues = function (xs) {
  return fp.map(function (car) {
    return car.dollar_value;
  }, cars);
}

let avarateDollarValue = fp.flowRight(_average, _dollarValues);
console.log(avarateDollarValue(cars));

// 练习 4: 使用 flowRight 写一个 sanitizenames()函数，返回一个下划线连接的小写字符串,把数组中的 name 转换为这种形式：例如 sanitizenames(["Hello World"]) => ["Hello World"]
let _underscore = fp.replace(/\W+/g, '_');

let sanitizenames = fp.map(fp.flowRight(fp.toLower, _underscore, fp.prop('name')))
console.log(sanitizenames(cars))



/*
4.根据数据练习以下四道题
*/
//练习 1: 使用 fp.add (x, y) 和fp.map(f, x）创建一个能让 functor 里的值增加的函数 ex1
let maybe = Maybe.of([5, 6, 1])
let ex1 = () => {
  return maybe.map(values => {
    return fp.map(fp.add(1), values);
  })
}
console.log(ex1());

// 练习 2: 实现一个函数 ex2, 能够使用 fp.first 获取列表的第一个元素
let xs = Container.of(['do', 'ray', 'me', 'fa', 'so', 'la', 'ti', 'do'])
let ex2 = () => {
  return xs.map(values => {
    return fp.first(values);
  })
}
console.log(ex2());

//练习 3: 实现一个函数 ex3, 使用 safeProp 和 fp.first 找到 user 的名字的首字母
let safeProp = fp.curry(function (x, o) {
  return Maybe.of(o[x])
})
let user = { id: 2, name: 'Albert' };
let ex3 = () => {
  return safeProp('name', user).map(value => fp.first(value))
}
console.log(ex3())

// 练习 4: 使用 Maybe 重写 ex4, 不要有 if 语句
let ex4 = function (n) {
  return Maybe.of(n).map(parseInt);
}
console.log(ex4('123'))
